package com.company.multithreads;

import java.util.ArrayList;

/**【原子性问题】
 * 案例02:
 *       5个线程各执行1000次 i++；
 *
 *     1. 多个线程对共享变量操作
 *     2. 在一次或多次操作中，要么所有的操作都执行并且不会受其他因素干扰而中断，要么所有的操作都不执行。
 *
 * 小结：在并发编程时，会出现原子性问题，当一个线程操作到一半的时候，另外的线程也有可能操作共享变量，干扰了前一个线程的操作。
 *
 * synchronized 能够保证在 同一时刻 最多只有一个线程执行该段代码，已达到保证并发安全的效果。
 *
 */

public class Test02AatomicityDemo {
    // 1. 定义一个共享变量 number
    private static int number = 0;
    // 创建锁对象
    private static Object obj = new Object();

    public static void main(String[] args) throws InterruptedException {
        // 2. 对 number 进行1000 的++ 操作
        Runnable increment = () -> {
            for (int i = 0; i < 1000; i++) {
                // 使用了synchronized之后就可以保证number++是一个原子操作
                synchronized (Test02AatomicityDemo.class) {
                    number++;
                }
//                synchronized (obj) {
//                    number++;
//                }

            }
        };

        // 3. 使用 5 个线程，并将5个线程放到List 集合中（目的是为了让5个线程都执行完）
        ArrayList<Thread> ts = new ArrayList<>();
        for (int i = 0; i < 5; i++) {
            Thread t = new Thread(increment);
            t.start();
            ts.add(t);
        }

        /* join() 的作用：https://blog.csdn.net/frankarmstrong/article/details/55504161
                API给出的解释是：Waits for this thread to die.
                main是主线程，在main中创建了thread线程，在main中调用了thread.join()，那么等thread结束后再执行main代码。
         */
        for (Thread t : ts) {
            t.join();
        }

        System.out.println("number = " + number);
    }
/**
     之前存在线程安全问题；
     即多次运行测试发现数据可能会小于5000；
     number = 5000;
     number = 4935;
     导致这种结果的原因是因为number++是由4条字节码指令进行组成的；
     并没有保证这4条字节码指令的原子性操作；
     接下来使用synchronized；synchronized需要一把锁对象；
     则创建一把锁；Object obj即可；
     那么使用了synchronized之后就可以保证number++是一个原子操作；
     再次多次运行就会发现number的取值不会进行变动了；而是直接就是5000；
 */

}

